// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "power.h"

#include <zircon/syscalls/system.h>

#include <acpica/acpi.h>
#include <ddk/debug.h>

void poweroff(void) {
  ACPI_STATUS status = AcpiEnterSleepStatePrep(5);
  if (status == AE_OK) {
    AcpiEnterSleepState(5);
  }
}

void reboot(void) {
  // Please do not use get_root_resource() in new code. See ZX-1467.
  zx_status_t status = zx_system_powerctl(get_root_resource(), ZX_SYSTEM_POWERCTL_REBOOT, NULL);
  if (status != ZX_OK)
    zxlogf(ERROR, "acpi: Failed to enter reboot: %d\n", status);
  AcpiReset();
}

void reboot_bootloader(void) {
  // Please do not use get_root_resource() in new code. See ZX-1467.
  zx_status_t status = zx_system_powerctl(get_root_resource(), ZX_SYSTEM_POWERCTL_REBOOT_BOOTLOADER, NULL);
  if (status != ZX_OK)
    zxlogf(ERROR, "acpi: Failed to enter bootloader reboot: %d\n", status);
  AcpiReset();
}

void reboot_recovery(void) {
  // Please do not use get_root_resource() in new code. See ZX-1467.
  zx_status_t status = zx_system_powerctl(get_root_resource(), ZX_SYSTEM_POWERCTL_REBOOT_RECOVERY, NULL);
  if (status != ZX_OK)
    zxlogf(ERROR, "acpi: Failed to enter recovery reboot: %d\n", status);
  AcpiReset();
}

zx_status_t suspend_to_ram(void) {
  zx_status_t status = ZX_OK;

  acpica_enable_noncontested_mode();

  // Please do not use get_root_resource() in new code. See ZX-1467.
  status = zx_system_powerctl(get_root_resource(), ZX_SYSTEM_POWERCTL_DISABLE_ALL_CPUS_BUT_PRIMARY,
                              NULL);
  if (status != ZX_OK) {
    zxlogf(ERROR, "acpi: Failed to shutdown CPUs: %d\n", status);
    goto cleanup;
  }

  ACPI_STATUS acpi_status = AcpiEnterSleepStatePrep(3);
  if (acpi_status != AE_OK) {
    zxlogf(ERROR, "acpi: Failed to prep enter sleep state: %x\n", acpi_status);
    // TODO: I think we need to do LeaveSleepState{Prep,} on failure
    status = ZX_ERR_INTERNAL;
    goto cleanup;
  }

  acpi_status = AcpiEnterSleepState(3);
  if (acpi_status != AE_OK) {
    status = ZX_ERR_INTERNAL;
    zxlogf(ERROR, "acpi: Failed to enter sleep state: %x\n", acpi_status);
    // Continue executing to try to get the system back to where it was
  }
  zxlogf(TRACE, "acpi: Woke up from sleep\n");

  acpi_status = AcpiLeaveSleepStatePrep(3);
  if (acpi_status != AE_OK) {
    status = ZX_ERR_INTERNAL;
    zxlogf(ERROR, "acpi: Failed to prep leave sleep state: %x\n", acpi_status);
  }

  acpi_status = AcpiLeaveSleepState(3);
  if (acpi_status != AE_OK) {
    status = ZX_ERR_INTERNAL;
    zxlogf(ERROR, "acpi: Failed to leave sleep state: %x\n", acpi_status);
  }

  zx_status_t status2;
cleanup:
  // Please do not use get_root_resource() in new code. See ZX-1467.
  status2 = zx_system_powerctl(get_root_resource(), ZX_SYSTEM_POWERCTL_ENABLE_ALL_CPUS, NULL);
  if (status2 != ZX_OK) {
    zxlogf(ERROR, "acpi: Re-enabling all cpus failed: %d\n", status2);
  }

  acpica_disable_noncontested_mode();

  zxlogf(INFO, "acpi: Finished processing suspend: %d\n", status);
  return status;
}
